<?php

namespace Wang\TonglianPay;

use App\Log;
use Curl\Curl;
use Exception;
use UnexpectedValueException;
use Wang\TonglianPay\encrypt\Encrypt;

class TongLianServer implements TongLianInterface
{
    /**
     * @var Encrypt
     */
    private $encrypt;

    /**
     * @var array
     */
    private $config;

    /**
     * @var Curl
     */
    private $curl;

    static public $isMobile;

    public function __construct(array $config)
    {
        $this->config = $config;
        $this->curl = container()->get(Curl::class);
        $this->encrypt = container()->get(Encrypt::class);
        self::$isMobile = true;
    }

    /**
     * 跳转app
     * @param array $data
     * @return array
     */
    public function jumpApp(array $data): array
    {
        $urlData = [ # H5
            0 => '/xieda/#/userInfo',        # 个人中心
            1 => '/xieda/#/myAsset',         # 我的资产
            2 => '/xieda/#/baoAsset',        # 宝类资产
            3 => '/xieda/#/recharge/' . $this->config['wxb']['bbCode'], # 充值
            4 => '/xieda/#/takeout',  # 提现
            5 => '/xieda/#/bindCardInfo',    # 绑定银行卡 开户
            6 => '/xieda/#/bankCardDetails',# 换绑银行卡/预留手机
            99 => '/xieda/#/addAddress',      # 联系地址
            100 => base64_decode(($data['whole_url']??'0')),#接口返回的完整链接 base64解密
        ];
        $urlPcData = [
            0 => '/account',                # 个人中心
            1 => '/myAsset',                # 我的资产
            2 => '/bao',                    # 宝类资产
            3 => '/bao/recharge',           # 充值
            4 => '/bao/takeout/000709_1000919900007268&20090110044142814',  # 提现
            5 => '/xieda/#/bindCardInfo',      # 绑定银行卡 开户
            99 => '/account/modifyUserAddress', # 联系地址
        ];
        $request = [
            'Request' => [
                'MobilePhone' => isset($data['phone']) ? $this->encrypt->webRsaEncrypt($data['phone'],$this->config['cert']['xr']['pub_key']) : '', # 181 3115 3490
                'Name' => isset($data['Name']) ? $this->encrypt->webRsaEncrypt($data['Name'],$this->config['cert']['xr']['pub_key']) : '', # 王小二
                'IdType' => '00',
                'IdNo' => isset($data['IdNo']) ? $this->encrypt->webRsaEncrypt($data['IdNo'],$this->config['cert']['xr']['pub_key']) : '', # self::RSA_encrypt('130185198908300541')
                'Accounts' => isset($data['Accounts']) ? $this->encrypt->webRsaEncrypt($data['Accounts'],$this->config['cert']['xr']['pub_key']) : '', # 1234552019384712343
                'BankCodes' => $data['BankCodes'] ?? '03040000', # 03040000
                'Flag' => $data['Flag'] ?? 1,
                'UserId' => $data['UserId'] ?? 0,
                'Timestamp' => date('YmdHis'),
                'URL' => self::$isMobile ?  $urlData[ $data['Url_key']??0 ] : $urlPcData[ $data['Url_key']??0 ],
                'FundCode' => $data['FundCode'] ?? '',
                'CallbackURL' => $data['CallbackURL'] ?? 'http://localhost:800/',
            ]
        ];
        $data = [
            'requestXml' => base64_encode($this->arrayToXml($request)),
            'signMsg' => $this->encrypt->sign($this->arrayToXml($request['Request'], false), $this->config['cert']['xr']['pri_key'], $this->config['cert']['xr']['pri_pass']),
        ];
        return ['url'=>$this->config['wxb']['app_host'],'data'=>$data];
    }
    public function setIsMobile($isMobile)
    {
        self::$isMobile = $isMobile;
    }
    /**
     * 绑卡开户推送
     * @param array $data
     * @return string
     */
    public function bindCard(array  $data): string
    {

        # RSA私钥解密AES密钥密文获得AES16位密钥，
        $key = $this->encrypt->rsaDecrypt(base64_decode($data['reqHead']['encrypted_key']), $this->config['cert']['xr']['pri_key'], $this->config['cert']['xr']['pri_pass']);
        # 使用AES密钥解密reqBody得到明文数据
        return rtrim($this->encrypt->aesDecrypt(base64_decode($data['reqBody']), $key));
    }

    /**
     * 绑卡返回
     * @param $serno
     * @param $success
     * @return false|string
     */
    public function noticeJson($serno,$success)
    {
        $key = $this->randomString(16);
        $respBody = json_encode($success ? ['TaskState' => 'succ'] : ['TaskState' => 'fail']);
        return json_encode([
            'respHead' =>
                [
                    'head_rtn_code' => $success ? "000000" : "100001",
                    'head_rtn_desc' => $success ? "处理成功" : "处理失败",
                    'head_resp_serno' => $serno,
                    'timestamp' => self::getUnixTimestamp(),
                    'head_signature' =>$this->encrypt->sign($respBody, $this->config['cert']['xr']['pri_key'], $this->config['cert']['xr']['pri_pass']),
                    'encrypted_key' => $this->encrypt->rsaEncrypt($key, $this->config['cert']['th']),
                ],
            'respBody' => $this->encrypt->aesEncrypt($respBody, $key),
        ]);
    }

    /**
     * 批量还款
     * @param $data
     * @return mixed|string|null
     */
    public function batchRepaymentFileUploadAction($data)
    {
        $msg = [
            'inst_id' => $this->config['wxb']['inst_id'],
            'trans_date' => date('Ymd'),
            'batch_no' => $data['batch'],
            'total_count' => $data['count'], # 交易笔数 最大5000笔
            'total_amount' => $data['amount'], # 交易金额
            'total_units' => $data['units'], # 交易份额
            'total_discount' => $data['discount'], # 营销金额
            'upload_file' => file_get_contents($data['file']),

        ];
        # 273016007654637568||202009011005189575|0|3|03040000|1|1234552019384712343||156|13333||||000709|8053000080530000||||||||
        # 申请方交易流水号|二级商户号|通联会员号|还款类型|支付模式|银行编码|账号类型|账号|联行号|交易币种|交易金额|营销金额|交易份额|现金账户代码|结算账户银行编码|结算账户账号|扩展字段 1|扩展字段 2|扩展字段 3|附加信息 1|户名|证件类型|证件号码|交易子账户
        # 读取文件明细并签名
        $msg['sign_code'] = $this->encrypt->sign($msg['upload_file'], $this->config['cert']['fund']['pri_key'] , $this->config['cert']['fund']['pri_pass']);
        $delimiter = uniqid();
        $this->curl->setHeader("Accept","*/*");
        $this->curl->setHeader("Content-type","multipart/form-data; boundary=$delimiter");
        $data = $this->buildData($msg, $delimiter);
        $url = $this->config['wxb']['host'] . UrlConfig::BATCH_REPAY;
        return $this->curl->post($url ,$data);
    }

    /**
     * 批量还款回盘下载
     * @param $data
     * @return mixed|string|null
     */
    public function batchRepaymentFileDownloadAction($data)
    {
        $msg = [
            'inst_id' => $this->config['wxb']['inst_id'],
            'trans_date' => $data['date'],
            'batch_no' => $data['orderId'],
        ];
        # 申请方交易流水号|受理方交易流水号|二级商户号|通联会员号|签约类型|供应商标识|银行生态圈机构接入规范编码|账户类型|账号|户名|证件类型|证件号码|手机号码|供应商编码|扩展字段 1|扩展字段 2|扩展字段 3|清算日期|交易返回码|交易返回信息|附加信息 1|交易子账号（开通单卡多账户返回）
        $msg['sign_code'] = $this->encrypt->sign($msg['inst_id'] . $msg['trans_date'] . $msg['batch_no'], $this->config['cert']['fund']['pri_key'] , $this->config['cert']['fund']['pri_pass']);
        $this->curl->setHeader("Accept","*/*");
        $this->curl->setHeader("Content-type","multipart/form-data");
        $url = $this->config['wxb']['host'] . UrlConfig::BATCH_REPAY_DOWNLOAD;
        return $this->curl->post($url ,$msg);
    }

    /**
     * 现金账户份额查询
     * @param $data
     * @return array|mixed|string[]
     * @throws Exception
     */
    public function AccountQuerySh($data)
    {
        try {
            $postData = $this->SignData('3002',$data);
            return $this->sendWebQuery($postData);
        }catch (Exception $exception) {
            throw new Exception($exception->getMessage());
        }
    }
    /**
     * 现金账户份额查询
     * @param $data
     * @return array|mixed|string[]
     * @throws Exception
     */
    public function AccountQuery($data)
    {
        try {
            $postData = $this->SignData('3004', [
                'req_trace_num' => $data['order_num'],
                'sign_num' => $data['sign_num'], # 生态圈身份标识
                'bnk_id' => $data['bnk_id']??"", # 银行编码
                'acct_type' => '1',
                'acct_num' => $data['acct_num']??"",
            ]);
            return $this->sendQuery($postData);
        }catch (Exception $exception){
            throw new Exception($exception->getMessage());
        }

    }

    /**份额账户购买金交所产品 4.22.5
     * @param $data
     * @param $trace
     * @return array|mixed|string[]
     */
    public function purchase($data, $trace)
    { #
        try {
            $postData = $this->signData('2085', [
                'req_trace_num' => $data['order_id'],       # 流水号
                'sign_num' => $data['sign_num'],            # 生态圈身份标识
                'purchase_type' => '0',                     # 购买
                'pay_mode' => '2',                          # 份额支付
                'bnk_id' => $data['bnk_id'],                # 银行编码
                'acct_type' => '1',                         # 账户类型 借记卡
                'acct_num' => $data['acct_num'],            # 账号
                'tel_num' => $data['tel_num'],              # 手机号
                'cur_type' => '156',                        # 人民币种类
                'amt_tran' => $data['amt_tran'],            # 交易金额 单位:分
                'pay_amount' => $data['pay_amount'],        # 支付金额 单位:分
                'promotion_fee' => $data['promotion_fee'],  # 营销金额 单位:分
                'prod_import_flag' => '0',                  # 产品引入方 机构引入
                'supply_inst_code' => $this->config['wxb']['supply_inst_code'],# 供应商编码
                'sep_detail' => json_encode($trace),
                'product_code_cash_acct' => $this->config['wxb']['bbCode'],#现金账户代码
                'order_num' => $data['order_id'],           # 交易订单号
                'resp_url' => $data['resp_url'],            # 回调地址
            ]);
            return $this->sendQuery($postData);
        } catch (UnexpectedValueException $exception) {
            throw new UnexpectedValueException($exception->getMessage());
        }
    }

    /**单笔预约购买份额预约购买申请 4.24.5
     * @param $data
     * @param $trace
     * @return array|mixed|string[]
     */
    public function reservePurchase($data, $trace)
    { # 单笔预约购买 4.24.5 份额预约购买申请

        try {
            $postData = $this->signData('2089', [
                'req_trace_num' => $data['order_id'],       # 流水号
                'sign_num' => $data['sign_num'],            # 生态圈身份标识
                'reserve_purchase_type' => '0',             # 预约购买
                'pay_mode' => '2',                          # 份额支付
                'frozen_acct_flag' => '1',                  # 冻结资金所在账户 现金账户
                'bnk_id' => $data['bnk_id'],                # 银行编码
                'acct_type' => '1',                         # 账户类型 借记卡
                'acct_num' => $data['acct_num'],            # 账号
                'tel_num' => $data['tel_num'],              # 手机号
                'cur_type' => '156',                        # 人民币种类
                'amt_tran' => $data['amt_tran'],            # 交易金额 单位:分
                'pay_amount' => $data['pay_amount'],        # 支付金额 单位:分
                'promotion_fee' => $data['promotion_fee'],  # 营销金额 单位:分
                'prod_import_flag' => '0',                  # 产品引入方 机构引入
                'supply_inst_code' => $this->config['wxb']['supply_inst_code'],# 供应商编码
                'sep_detail' => json_encode($trace),
                'product_code_cash_acct' => $this->config['wxb']['bbCode'],#现金账户代码
                'order_num' => $data['order_id'],           # 交易订单号
                'resp_url' => $data['resp_url'],            # 回调地址
            ]);
            return $this->sendQuery($postData);
        } catch (UnexpectedValueException $exception) {
            throw new UnexpectedValueException($exception->getMessage());
        }
    }

    /**
     * 单笔还款交易 4.30.3 还款到余额
     * @param $data
     * @return array|mixed|string[]
     */
    public function repayment($data)
    {
        $info = [
            'req_trace_num' => $data['orderid'],
            'sign_num' => $data['sign_num'], # 生态圈身份标识 通联会员号
            'repayment_type' => 0,
            'pay_mode' => 2,
            'bnk_id' => $data['bank_id'],
            'acct_type' => 1,
            'acct_num' => $data['acct_num'],
            'tel_num' => $data['tel_num'],
            'cur_type' => 156,
            # 还款金额
            'amt_tran' => $data['amt_tran'], #''
            'prod_import_flag' => 0,
            'supply_inst_code' => "",
            'sep_detail' => json_encode($data['trace']),
            'product_code_cash_acct'=>$this->config['wxb']['bbCode'],
            'resp_url' => $data['resp_url'],
            'addtnl_data1' => $data['addtnl_data1'] ?? "",
            'summary' => $data['summary'] ?? "",
        ];
        try {
            return $this->sendQuery($this->signData('2294', $info));
        }catch (UnexpectedValueException $exception){
            throw new UnexpectedValueException($exception->getMessage());
        }
    }

    /**
     * 联机下载对账文件
     * @param $data
     * @param $filePath
     * @return array
     */
    public function orgDownloadReconFile($data,$filePath): array
    {
        $msg = [
            'inst_id' => $this->config['wxb']['inst_id'],
            'trans_date' => $data['trans_date'],
            'file_type' => $data['file_type'],
            'batch_no' => '',//sprintf('%s_JR_%.6d_%2s', self::$inst_id, $data['trans_date'], '01'),
        ];
        $msg['sign_code'] = $this->encrypt->sign($msg['inst_id'] . $msg['trans_date'] . $msg['file_type'] . $msg['batch_no'], $this->config['cert']['fund']['pri_key'] , $this->config['cert']['fund']['pri_pass']);
        return $this->download($msg,$filePath);
    }

    /**
     * 基金图片地址
     * @param $Parameters
     * @return string
     */
    public function jiJinImageUrl($Parameters): string
    {
        ksort($Parameters);
        $sign = '';
        foreach ($Parameters as $k => $v) {
            $sign .= $k . "=" . $v . "&";
        }
        $sign = rtrim($sign, '&');
        $signature = $this->encrypt->sign($sign,$this->config['cert']['xr']['pri_up_key'],$this->config['cert']['xr']['pri_pass']);
        $signature = urlencode($signature);
        return $this->config['fund']['imageHost'].'/uploads/downloadServlet?'.$sign.'&sign='.$signature;
    }

    /**
     * 基金余额查询
     * @param $body
     * @param $head_tran_code
     * @param $url
     * @return mixed|string|null
     */
    public function jiJinSearch($body,$head_tran_code,$url)
    {
        ksort($body);# 拼装加密字符串
        $timestamp = time().'000';# 时间戳 拼凑
        $sign = '';
        foreach ($body as $v){
            $sign .= $v.'|';
        }
        $sign .= $timestamp;
        $data = json_encode([
            'reqHead' => [
                'head_channel_code' => $this->config['wxb']['old_inst_id'],
                'head_channel_code_sub' => $this->config['wxb']['old_inst_id'] . '04',
                'head_channel_date' => date('Ymd'),
                'head_channel_time' => date('His'),
                'head_tran_code' => $head_tran_code,
                'head_signature' => $this->encrypt->sign($sign,$this->config['cert']['xr']['pri_key'],$this->config['cert']['xr']['pri_pass']),
                'sub_sysmbcode' => '00',
                'timestamp' => $timestamp,
            ],
            'reqBody' => $body,
        ]);
        $this->curl->setHeader("Accept","*/*");
        $this->curl->setHeader("Content-type"," application/json;charset=UTF-8");
        $urls = $this->config['fund']['host'].$url;
        return $this->curl->post($urls ,$data);
    }

    /**
     * 文件下载
     * @param $data
     * @param $filePath
     * @return array
     */
    private function download($data,$filePath): array
    {
        $url = $this->config['wxb']['host'] . UrlConfig::ORG_DOWNLOAD_FILE;
        $this->curl->setHeader("Accept","*/*");
        $this->curl->setHeader("Content-type","application/x-www-form-urlencoded");
        $request =$this->curl->post($url ,$data);
        if ($request){
            if (!is_dir($filePath)) mkdir($filePath, 0777, true);
            file_put_contents($filePath . '/' . $data['trans_date'] . '_' . $data['file_type'] . '.zip', $request);
            return ['status' => 200, 'message' => '下载成功'];
        }else{
            throw new UnexpectedValueException('接口请求失败！');
        }
    }

    private function sendQuery($data)
    {
        $url = $this->config['wxb']['host'] . UrlConfig::TL_API;
        $maxRetries = 2;  // 最多重试 2 次
        $attempt = 0;

        while ($attempt < $maxRetries) {
            $attempt++;
            $this->curl->setHeader("Accept", "*/*");
            $this->curl->setHeader("Content-Type", "application/x-www-form-urlencoded");

            $request = $this->curl->post($url, $data);

            // 请求成功，尝试解析
            if ($request) {
                try {
                    return self::checkRequestData($request);
                } catch (\Exception $e) {
                    // 数据格式错误，可能是服务端问题，可重试
                    Log::get("sendQuery:")->warning("第 {$attempt} 次响应数据验证失败", [
                        'error' => $e->getMessage(),
                        'response' => $request,
                        'url' => $url
                    ]);
                    if ($attempt >= $maxRetries) {
                        throw $e;
                    }
                    sleep(1);
                    continue;
                }
            }

            // 请求失败：检查是否是可重试的错误（如 DNS、超时）
            if ($this->curl->error) {
                $errorCode = $this->curl->errorCode;
                $errorMessage = $this->curl->errorMessage;

                Log::get("sendQuery:")->warning("第 {$attempt} 次请求失败", [
                    'errorCode' => $errorCode,
                    'errorMessage' => $errorMessage,
                    'url' => $url
                ]);

                // 只对网络类错误重试
                if (in_array($errorCode, [6, 7, 28, 52, 56]) && $attempt < $maxRetries) {
                    sleep(1); // 避免密集请求
                    continue;
                }
            }

            // 不重试的情况：break 跳出循环
            break;
        }

        // 所有尝试都失败了
        Log::get("sendQuery:")->error("最终请求失败", [
            'errorCode' => $this->curl->errorCode ?? null,
            'errorMessage' => $this->curl->errorMessage ?? null,
            'response' => $this->curl->response,
            'url' => $url,
            'data' => $data
        ]);

        throw new \UnexpectedValueException('接口请求失败！');
    }

    /**
     * @param $data
     * @return array|mixed|string[]
     */
    private function sendWebQuery($data)
    {
        $url = $this->config['wxb']['host'] . '/ccdp-acquire-web/service/acquireAction.action';
        $this->curl->setHeader("Accept","*/*");
        $this->curl->setHeader("Content-type","application/x-www-form-urlencoded");
        $request =$this->curl->post($url ,$data);
        if ($request){
            return self::checkRequestData($request);
        }else{
            throw new UnexpectedValueException('接口请求失败！');
        }
    }

    /**
     * @param string $data
     * @return array|mixed|string[]
     */
    public function checkRequestData(string $data = '')
    {
        if ($data == '') return ['code' => '500', 'msg' => '获取返回内容失败'];
        $xml = $this->xmlToObj(base64_decode($data));
        $oldResponse = $this->reverseStrChr(
            $this->encrypt->desDecrypt(
                base64_decode($xml->EncryptedText),
                $this->encrypt->rsaDecrypt(base64_decode($xml->KeyInfo->EncryptedKey), $this->config['cert']['fund']['pri_key'] , $this->config['cert']['fund']['pri_pass'])
            ));
        $response = $this->xmlToObj(@iconv("UTF-8", "gb2312//TRANSLIT", $oldResponse));
        if (!$response) {
            $xml= str_replace('encoding="GBK"','encoding="UTF-8"',$oldResponse);
            $response = simplexml_load_string($xml,'SimpleXMLElement', LIBXML_NOCDATA);
        }
        $newXML = str_replace('<sign_code>' . $response->head->sign_code . '</sign_code>', '', $oldResponse);
        if (!$this->encrypt->verify($newXML, $response->head->sign_code, $this->config['cert']['fund']['pub_key'])) {
            Log::get("checkRequestData:")->info("通联验签信息",['data' => $data,'newXML' => $newXML,'oldResponse' => $oldResponse,'sign_code' => $response->head->sign_code ?? '']);
            return ['code' => '500', 'msg' => '内部签名失败'];
        }
        return $this->objToArray($response);
    }

    /**
     * @param $param
     * @param $delimiter
     * @return string
     */
    private function buildData($param, $delimiter): string
    {
        $data = '';
        $eol = "\r\n";
        $upload = $param['upload_file'];
        unset($param['upload_file']);

        foreach ($param as $name => $content) {
            $data .= "--" . $delimiter . "\r\n"
                . 'Content-Disposition: form-data; name="' . $name . "\"\r\n\r\n"
                . $content . "\r\n";
        }
        // 拼接文件流
        $data .= "--" . $delimiter . $eol
            . 'Content-Disposition: form-data; name="upload_file"; filename="' . $param['batch_no'] . '.txt"' . "\r\n"
            . 'Content-Type:text/plain' . "\r\n\r\n";

        $data .= $upload . "\r\n";
        $data .= "--" . $delimiter . "--\r\n";
        return $data;
    }

    private function getUnixTimestamp(): Float
    { # 毫秒时间戳
        list($s1, $s2) = explode(' ', microtime());
        return (float)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
    }

    public function arrayToXml($arr, $head = true) :string
    {
        $xml = $head ? '<?xml version="1.0" encoding="GBK"?>' : '';
        foreach ($arr as $k => $v) $xml .= "<$k>" . (is_array($v) ? $this->arrayToXml($v, false) : $v) . "</$k>";
        return $xml;
    }

    private function xmlToObj($str)
    {
        return simplexml_load_string($str, 'SimpleXMLElement', LIBXML_NOCDATA);
    }

    private function objToArray($obj)
    {
        if (is_object($obj)) $obj = (array)$obj;
        if (is_array($obj)) foreach ($obj as $k => $v) $obj[$k] = is_object($v) ? ($v->count() ? $this->objToArray($v) : '') : $this->objToArray($v);
        return $obj;
    }

    private function reverseStrChr($haystack)
    {
        $pos = strrpos($haystack, ">");
        if ($pos === false) {
            return $haystack;
        }
        return substr($haystack, 0, $pos + 1);
    }

    /**
     * 随机字符串
     * @param int $length
     * @param string $charset
     * @return string
     */
    function randomString(int $length = 24, string $charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') : string
    {
        $randomString = '';
        for ($i = 0; $i < $length; $i++) {
            try {
                $randomString .= $charset[random_int(0, strlen($charset) - 1)];
            } catch (Exception $e) {
                throw new UnexpectedValueException($e->getMessage());
            }
        }
        return $randomString;
    }

    /**
     * 数据加密
     * @param String $code
     * @param array $data
     * @return array
     */
    private function signData(String $code, Array $data) :array
    { # 数据签名
        $key = $this->randomString();
        $request = $this->setData($data, $code);
        $xml = $this->arrayToXml($request);
        $request['transaction']['head']['sign_code'] = $this->encrypt->sign($xml, $this->config['cert']['fund']['pri_key'] , $this->config['cert']['fund']['pri_pass']);
        $desStr = $this->pkcs5Pad($this->arrayToXml($request));
        $msg = [
            'STSPackage' => [
                'EncryptedText' => $this->encrypt->desEncrypt($desStr, $key),
                'KeyInfo' => [
                    'ReceiverX509CertSN' => $this->config['wxb']['CertSN'],
                    'EncryptedKey' => $this->encrypt->rsaEncrypt($key,  $this->config['cert']['fund']['pub_key']),
                ],
            ],
        ];
        return ['msgPlain' => base64_encode($this->arrayToXml($msg, false))];
    }

    private function signDataBuildQuery(String $code, Array $data)
    { # 数据签名
        $key = $this->randomString();
        $request = $this->setData($data, $code);
        $xml = $this->arrayToXml($request);
        $request['transaction']['head']['sign_code'] = $this->encrypt->sign($xml, $this->config['cert']['fund']['pri_key'] , $this->config['cert']['fund']['pri_pass']);
        $desStr = $this->pkcs5Pad($this->arrayToXml($request));
        $msg = [
            'STSPackage' => [
                'EncryptedText' => $this->encrypt->desEncrypt($desStr, $key),
                'KeyInfo' => [
                    'ReceiverX509CertSN' => $this->config['wxb']['CertSN'],
                    'EncryptedKey' => $this->encrypt->rsaEncrypt($key,  $this->config['cert']['fund']['pub_key']),
                ],
            ],
        ];
        //        return http_build_query(['msgPlain' => base64_encode($this->arrayToXml($msg, false))]);
        return base64_encode($this->arrayToXml($msg, false));
    }

    private function pkcs5Pad($text) : string
    {
        $pad = 8 - (strlen($text) % 8);
        return $text . str_repeat(chr($pad), $pad);
    }

    /**
     * 设置数据
     * @param array $request
     * @param String $code
     * @return array[]
     */
    public function setData(Array $request, String $code): array
    {
        return [
            'transaction' =>
                [
                    "head" => [
                        'processing_code' => $code,
                        'inst_id' => $this->config['wxb']['inst_id'],
                        'trans_date' => date('Ymd'),
                        'trans_time' => date('His'),
                        'ver_num' => $this->config['wxb']['ver_num'],
                    ],
                    "request" => $request,
                ],
        ];
    }

    /**
     * 快捷开户
     * @param $data
     * @return array|mixed|string[]
     * @throws Exception
     */
    public function openQuick($data)
    {
        try {
            $postData = $this->SignData('1087', $data);
            return $this->sendQuery($postData);
        }catch (Exception $exception){
            throw new Exception($exception->getMessage());
        }
    }

    /**
     * 更换银行卡
     * @param $data
     * @return array|mixed|string[]
     * @throws Exception
     */
    public function changeBank($data)
    {
        try {
            $postData = $this->SignData('1088', $data);
            return $this->sendQuery($postData);
        }catch (Exception $exception){
            throw new Exception($exception->getMessage());
        }
    }

    /**
     * 更换手机号
     * @param $data
     * @return array|mixed|string[]
     * @throws Exception
     */
    public function changePhone($data)
    {
        try {
            $postData = $this->SignData('1090', $data);
            return $this->sendQuery($postData);
        }catch (Exception $exception){
            throw new Exception($exception->getMessage());
        }
    }

    /**
     * 发送验证码
     * @param $data
     * @return array|mixed|string[]
     * @throws Exception
     */
    public function Code($data)
    {
        try {
            $postData = $this->SignData('3010', $data);
            return $this->sendQuery($postData);
        }catch (Exception $exception){
            throw new Exception($exception->getMessage());
        }
    }

    /**
     * 充值
     * @param $data
     * @return array|mixed|string[]
     * @throws Exception
     */
    public function rechargeBank($data)
    {
        try {
            $postData = $this->SignData('2080', $data);
            return $this->sendQuery($postData);
        }catch (Exception $exception){
            throw new Exception($exception->getMessage());
        }
    }

    /**
     * 提现
     * @param $data
     * @param $type
     * @return array|mixed|string[]
     * @throws Exception
     */
    public function cash($data,$type)
    {
        try {
            $code = $type == 0 ? '2280': '2290';
            $postData = $this->SignData($code, $data);
            return $this->sendQuery($postData);
        }catch (Exception $exception){
            throw new Exception($exception->getMessage());
        }
    }

    public function rechargeWeb($data)
    {
        try {
            $url = $this->config['wxb']['host'] . '/AppStsWeb/service/acquireAction.action';
            $postData = $this->signDataBuildQuery('2080', $data);
//            $data = $this->sendQuery($postData);
            return ['url' => $url, 'data' => $postData];
        }catch (Exception $exception){
            throw new Exception($exception->getMessage());
        }
    }

    public function encryptVerify($data, $sign)
    {
        return $this->encrypt->verify($data,$sign,$this->config['cert']['th']);
    }

    public function returnPack($serno,$code,$msg,$body)
    {
        $key = $this->randomString(16);
        $respBody = json_encode($body);
        return json_encode([
            'respHead' =>
                [
                    'head_rtn_code' => $code,
                    'head_rtn_desc' => $msg,
                    'head_resp_serno' => $serno,
                    'timestamp' => self::getUnixTimestamp(),
                    'head_signature' => $this->encrypt->sign($respBody, $this->config['cert']['xr']['pri_key'], $this->config['cert']['xr']['pri_pass']),
                    'encrypted_key' => $this->encrypt->rsaEncrypt($key, $this->config['cert']['th']),
                ],
            'respBody' => $this->encrypt->aesEncrypt($respBody, $key),
        ]);
    }
}